const EventEmitter = require('events').EventEmitter;
const Websocket = require("ws");
const url = require('url');

const Logger = require("../modules/logger.js");
const { Base_ClientsManager, Base_ClientManager } = require('./class/Base_ClientsManager.js');


let { log } = console;

class WebsocketServer extends EventEmitter{
    constructor({name='123', port, connectKey, ClientsManager, ClientManager, enableConsoleCmd=true, heartBeatInterval=10000, heartBeatTimeoutCount=2}){
        super();
        // 基础传入参数
        this.name = name;
        this.port = port;
        this.connectKey = connectKey;
        this.enableConsoleCmd = enableConsoleCmd;
        this.heartBeatInterval = heartBeatInterval;
        this.heartBeatTimeoutCount = heartBeatTimeoutCount;
        this._ClientsManager = ClientsManager;
        this._ClientManager = ClientManager;

        // WSS服务端专用日志记录器                     
        this.logger = new Logger(name);
        // 管理连接到server的客户端          
        this.clientsManager = ClientsManager ? new ClientsManager() : new Base_ClientsManager();     
        
        this.cache = {};
        // 自定义监听器存在这里
        //this.listener = new Map(); //{"eventName": [callback1,callback2,...]}

        // ws模块原wss对象, 启动wss服务
        this.wss = null;

        // 控制台输入
        if (enableConsoleCmd){
            this.consoleCmd = new Map();
            this.consoleCmd.set('help', {descreption: '命令帮助', func: ()=>{
                let res = `  # ${this.name}指令帮助 #`;
                this.consoleCmd.forEach((value, key)=>{
                    res += `\n - ${key}: ${value.descreption}`;
                });
                console.log(res);
            }});
            this.consoleCmd.set('stop', {descreption: '关闭服务端', func: ()=>{
                this.logger.info('Websocket server quit.');
                process.exit();
            }});
            
            let that = this;
            process.stdin.resume();
            process.stdin.setEncoding('utf8');
            process.stdin.on('data', function (text) {
                let cmd = text.split(/\s+/)[0];
                let args = text.split(/\s+/).slice(1);
                if (that.consoleCmd.has(cmd)){
                    that.consoleCmd.get(cmd)['func'](args);
                }else{
                    console.error(`"${cmd}" not a command`);
                }
            });
        }
    }

    start(){
        this.wss = new Websocket.Server({
            host:"0.0.0.0",
            port: this.port,
            verifyClient: (info)=>{
                let res = false;//拒绝
                let params = url.parse(info.req.url, true).query;
                if (params["key"] == this.connectKey) {
                    res = true;//通过
                }
                return res;
            }
        });
        // 确保事件监听器在服务器实例创建后立即注册
        this.wss.on('listening', () => {
            const address = this.wss.address();
            this.logger.info(`${this.name} 已启动 地址：${address.address} |协议: ${address.family} |端口: ${address.port}`);
            this.emit('serverstart');
        });
        this.wss.on('error', (error) => {
            this.logger.error(`${this.name} 启动失败: ${error.message}`);
        });
        this.wss.on('connection', (ws)=>this.#connection(ws));
    }

    close(){
        this.wss.clients.forEach((ws) => {
            ws.close(1000, '● WS协调服务端关闭');
        });
        this.wss.close();
        this.logger.info('● WS协调服务端已关闭');
    }

    #connection(ws){
        // 开启心跳
        ws.isAlive = true;
        ws.on('pong', ()=>{
            ws.isAlive = true;
        });
        let that = this;
        let heartBeatTimeoutCount = 0;
        ws.pingPongInterval = setInterval(function ping() {
            if (ws.isAlive === false) {
                heartBeatTimeoutCount++;
                that.logger.warn(`心跳响应失败 | 第 ${heartBeatTimeoutCount} 次 | ${thisClient.name} | id: ${thisClient.id}`);
                if (heartBeatTimeoutCount >= that.heartBeatTimeoutCount){
                    that.logger.error(`心跳超时断开 | 第 ${heartBeatTimeoutCount} 次 | ${thisClient.name} | id: ${thisClient.id}`);
                    clearInterval(ws.pingPongInterval);
                    return ws.terminate();
                }
            }
            heartBeatTimeoutCount = 0;
            ws.isAlive = false;
            ws.ping();
        }, that.heartBeatInterval);
  
        // 重包装为客户端(管理器)对象
        let thisClient = this._ClientManager ? new this._ClientManager(ws) : new Base_ClientManager(ws);
        this.clientsManager.add(thisClient);

        this.emit('connection', thisClient);

        ws.on('message', (message_raw)=>{
            this.emit('message', thisClient, message_raw);
        });
        ws.on('close', (code ,reason)=>{
            clearInterval(ws.pingPongInterval);
            ws.isAlive = false;
            this.emit('close', thisClient, code, reason);
            this.logger.warn(`<< WS客户端断开连接 | ${thisClient.name} | id: ${thisClient.id}`);
            this.clientsManager.remove(thisClient);
        });
        ws.on('error', (error)=>{
            this.emit('error', thisClient, error);
        });
        this.logger.info(`>> WS客户端建立连接 | id: ${thisClient.id}`);
    }
}
module.exports = WebsocketServer;

